/*
 * Copyright (C) 2006 Roman Krashanitsa
 *
 * @author Roman Krashanitsa
 *
 * This file is part of paparazzi.
 *
 * paparazzi is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * paparazzi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with paparazzi; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA. 
 *
 */


package pprz.components;

import java.awt.Dimension;
import java.awt.Scrollbar;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import net.jscience.math.MathFP;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import pprz.data_components.Aircraft;
import pprz.data_components.Device;
import pprz.data_components.Message;
import pprz.data_components.MsgProtocol;
import pprz.data_components.PprzMsgProtocol;
import pprz.data_components.UplinkDevice;
import pprz.data_components.events.DownlinkMessageEvent;
import pprz.data_components.events.DownlinkMessageEventListener;

public class Settings extends JSplitPane 
implements DownlinkMessageEventListener, ActionListener, 
TreeSelectionListener {

	public class ITag implements ActionListener, AdjustmentListener {
//		protected int id;
		protected String name;
		protected String propname;
		protected String varname;
		protected String shortname;
		protected float min, max, step, cur_val, ac_val;

		protected JScrollBar ranger;
		protected JLabel lMin,lMax,lName,lAcVal;
		protected JTextField lVal; 
		protected JPanel panel;
		protected JButton bSend;
		
		protected Settings parent;
		
		protected int index; // index of this variable
		

		public ITag(String n, Attributes a, Settings parent, int index) {
			name = n;
			this.parent=parent;
			this.index=index;
			ac_val=0;
			//attr = a;
//			attr=new AttributesImpl(a);
			for (int i = 0; i < a.getLength(); i++) {
				String aname = a.getQName(i);
				String value = a.getValue(i);
				if (aname.equalsIgnoreCase("min")) {
					min=Float.valueOf(value);
				} else if (aname.equalsIgnoreCase("max")) {
					max=Float.valueOf(value);
				} else if (aname.equalsIgnoreCase("step")) {
					step=Float.valueOf(value);
				} else if (aname.equalsIgnoreCase("var")) {
					varname=value;
				} else if (aname.equalsIgnoreCase("shortname")) {
					shortname=value;
				} else if (aname.equalsIgnoreCase("name")) {
					propname=value;
				}
			}
			if (varname!=null)
			{
				int steps=Math.round((max-min)/step)+1;
				ranger = new JScrollBar(Scrollbar.HORIZONTAL, steps/2, 1, 0, steps);
				ranger.setUnitIncrement(1);
				ranger.setVisibleAmount(1);
				ranger.addAdjustmentListener(this);
				cur_val=min+ranger.getValue()*step;
				if (shortname!=null)
					lName=new JLabel(String.valueOf(shortname));
				else lName=new JLabel(String.valueOf(varname));
				lMin=new JLabel(String.valueOf(min));
				lMax=new JLabel(String.valueOf(max));
				lVal=new JTextField(String.valueOf(cur_val));
				lVal.setMaximumSize(new Dimension(100,20));
				lAcVal=new JLabel(String.valueOf(ac_val));
				bSend=new JButton("send"); bSend.addActionListener(this);
				panel=new JPanel();
				panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
				panel.add(lName); lName.setLocation(ranger.getWidth()/2, 0);
				panel.add(ranger); ranger.setLocation(0, lName.getHeight());
				panel.add(lMin); lMin.setLocation(0, ranger.getHeight()+lName.getHeight());
				panel.add(lMax); lMax.setLocation(ranger.getWidth(), ranger.getHeight()+lName.getHeight());
				panel.add(lVal); lVal.setLocation(ranger.getWidth()/2, ranger.getHeight()+lName.getHeight());
				panel.add(lAcVal); lVal.setLocation(ranger.getWidth()/2, ranger.getHeight()+lName.getHeight()+lVal.getHeight());
				panel.add(bSend);
				
			}
		}
		
		public void actionPerformed(ActionEvent arg0) {
				System.out.println("Sending "+(shortname==null?varname:shortname)+"="+String.valueOf(cur_val)+" to "+String.valueOf(parent.ac));
				if (parent.acInfo.get(parent.ac).getUplink()!=null) //airplane has uplink
				{
					MsgProtocol p=parent.acInfo.get(parent.ac).getUplink()._dec;
					Device d=parent.acInfo.get(parent.ac).getUplink()._dev;
					//send new value
					if (p.getClass()==PprzMsgProtocol.class && d instanceof UplinkDevice) {
						((PprzMsgProtocol)p).encode_set_var((UplinkDevice)d,parent.ac, index-1, cur_val);
					}
				}
		}
		
		/* (non-Javadoc)
		 * @see java.awt.event.AdjustmentListener#adjustmentValueChanged(java.awt.event.AdjustmentEvent)
		 */
		public void adjustmentValueChanged(AdjustmentEvent arg0) {
			if (arg0.getSource().equals(ranger)) {
				cur_val=min+ranger.getValue()*step;
				lVal.setText(String.valueOf(cur_val));
			}
				
		}

		public String getName() {
			return name;
		}

		public String getAttributesAsString() {
			StringBuffer buf = new StringBuffer(256);
			if (propname!=null) 
				buf.append(propname);
			else if (varname!=null)
				buf.append(varname);
			return buf.toString();
		}

		public String toString() {
			String a = getAttributesAsString();
			return ("".equals(a)?name:a);
		}
		
		public void setAcVal(float v)
		{
			ac_val=v;
			lAcVal.setText(String.valueOf(ac_val));
		}
		public void setVal(float v)
		{
			cur_val=v;
			lVal.setText(String.valueOf(cur_val));
		}

	} //class ITag
	
	public class Tree_Vars 
	{
		public JTree tree;
		public Vector<ITag> vars;
		
		public Tree_Vars(JTree tree, Vector<ITag> vars)
		{this.tree=tree; this.vars=vars;}
	} 
	
	public class XMLIconTreeHandler extends DefaultHandler {
		protected DefaultMutableTreeNode root, currentNode;
		protected Vector<ITag> vars;
		protected Settings parent;
		
		int index;
		
		public XMLIconTreeHandler(Settings parent)
		{
			super();
			this.parent=parent;
			index=1;
		}
		
		public DefaultMutableTreeNode getRoot() {
			return root;
		}

		// SAX Parser Handler methods...
		public void startElement(String namespaceURI, String lName, // local name
				String qName, // qualified name
				Attributes attrs) throws SAXException 
		{
			String eName = lName; // element name
			ITag t;
			if ("".equals(eName))
				eName = qName;
			if (attrs.getIndex("VAR")==-1 && attrs.getIndex("var")==-1)
			{
				t = new ITag(eName, attrs, parent,0);
			}
			else 
			{
				t = new ITag(eName, attrs, parent,index++);
			}
			DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(t);
			if (currentNode == null) {
				root = newNode;
				vars=new Vector<ITag>();
			} else {
				// must not be the root node...
				currentNode.add(newNode);
			}
			if (t.varname!=null) vars.add(t); //add to the list of variables
			currentNode = newNode;
		}

		public void endElement(String namespaceURI, String sName, // simple name
				String qName // qualified name
		) throws SAXException {
			currentNode = (DefaultMutableTreeNode) currentNode.getParent();
		}

		public void characters(char buf[], int offset, int len)
				throws SAXException {
//			String s = new String(buf, offset, len).trim();
//			((ITag) currentNode.getUserObject()).addData(s);
		}
	}
	
	protected static final long serialVersionUID = 1L;
	protected JTree tree;
	protected HashMap<Integer,Tree_Vars> acSettings;
//	protected Link link;
	HashMap<Integer,Aircraft> acInfo;
	protected int ac; //selected aircraft (set by parent or as a result of action)
	

	public Tree_Vars parse(String filename) {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		XMLIconTreeHandler handler = new XMLIconTreeHandler(this);
		try {
			// Parse the input
			SAXParser saxParser = factory.newSAXParser();
			if (filename!=null) saxParser.parse(new File(filename), handler);
		} catch (Exception e) {
			System.err.println("File Read Error: " + e);
			e.printStackTrace();
			return new Tree_Vars(new JTree(new DefaultMutableTreeNode("error")),handler.vars);
		}
		return new Tree_Vars(new JTree(handler.getRoot()),handler.vars);
	}

	protected JScrollPane left;
	protected JScrollPane right;
	
	public Settings(HashMap<Integer,Aircraft> acInfo) throws IOException {
		super();
		this.acInfo=acInfo;
		acSettings=new HashMap<Integer,Tree_Vars> ();
		left=new JScrollPane();
		right=new JScrollPane();
		this.setLeftComponent(left);
		this.setRightComponent(right);
		setDividerLocation(150);
	}

	protected void check_add(int ac)
	{
		if (!(acSettings.containsKey(ac)))
		{
			Aircraft a=acInfo.get(ac);
			String s=a.get_settings();
			//TreeModel t=parse(s);
			acSettings.put(ac, parse(s));
		}
	}
	public void selectAC(int ac)
	{
		this.ac=ac;
		check_add(ac);
		tree=acSettings.get(ac).tree;
		tree.addTreeSelectionListener(this);
		left.setViewportView(tree);
	}
	
	
	public void messageEvent(DownlinkMessageEvent ev) {
		Message m = ev.getMessage();
//		byte buf[]=m.payload();
		int ac=m.acRec().get_id();
		//process only downlink messages id=10 from currect AC
		if (m.msgClass()!=0 || m.id()!=31) return; // process only settings messages
		assert(ac!=0);
		check_add(ac); // add aircraft is needed
		Long lll=m.getFields().get(0)[0]; //get variable id
		int llll=(int)MathFP.toLong((lll));
		ITag tt=acSettings.get(ac).vars.get(llll);
		Long ll=m.getFields().get(1)[0]; // get variable value
		tt.setAcVal(MathFP.toLong(ll)/1.0E8f);
		
	}

	public void actionPerformed(ActionEvent arg0) {
		if (arg0.getSource().getClass() == AcInfoContainer.class)
			selectAC(((AcInfoContainer) arg0.getSource()).getSelected());	
	}

	/* (non-Javadoc)
	 * @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent)
	 */
	public void valueChanged(TreeSelectionEvent arg0) {
		if (!(arg0.getPath().getLastPathComponent().getClass()==DefaultMutableTreeNode.class))
			return;
		DefaultMutableTreeNode n=(DefaultMutableTreeNode)arg0.getPath().getLastPathComponent();
		if (!n.isLeaf()) return;
		right.setViewportView(((ITag)(n.getUserObject())).panel);
	}	
	
}
